#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QMessageBox>

#include "newconnectiondialog.h"
#include "dbviewercommon.h"
#include "tablewidget.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    qInfo() << "start app:" << APP_VERSION;
    QString configPath = qApp->applicationDirPath() + "/config.ini";
    m_settings = new QSettings(configPath, QSettings::IniFormat, this);
    ui->setupUi(this);
    ui->treeWidget->setColumnCount(1);
    initSignalSlots();
    initTreeWidget();
    initStatusBar();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::onTabCloseRequested(int index)
{
    ui->tabWidget->removeTab(index);
}

void MainWindow::onAddNewConnectionMysql()
{
    newConnection(DB_MYSQL);
}

void MainWindow::onAddNewConnectionSqlServer()
{
    newConnection(DB_SQLSERVER);
}

void MainWindow::onAddNewConnectionAccess()
{
    newConnection(DB_ACCESS);
}

void MainWindow::onAddNewConnectionSqlite()
{
    newConnection(DB_SQLITE);
}

void MainWindow::onAddNewConnection()
{
    QMenu menu(this);
    QAction actionMysql(DbViewerCommon::getDbTypeName(DB_MYSQL), this);
    actionMysql.setIcon(QIcon(DbViewerCommon::getConnectionIcon(DB_MYSQL, false)));
    connect(&actionMysql, &QAction::triggered, this, &MainWindow::onAddNewConnectionMysql);
    menu.addAction(&actionMysql);

    QAction actionSqlserver(DbViewerCommon::getDbTypeName(DB_SQLSERVER), this);
    actionSqlserver.setIcon(QIcon(DbViewerCommon::getConnectionIcon(DB_SQLSERVER, false)));
    connect(&actionSqlserver, &QAction::triggered, this, &MainWindow::onAddNewConnectionSqlServer);
    menu.addAction(&actionSqlserver);

    QAction actionAccess(DbViewerCommon::getDbTypeName(DB_ACCESS), this);
    actionAccess.setIcon(QIcon(DbViewerCommon::getConnectionIcon(DB_ACCESS, false)));
    connect(&actionAccess, &QAction::triggered, this, &MainWindow::onAddNewConnectionAccess);
    menu.addAction(&actionAccess);

    QAction actionSqlite(DbViewerCommon::getDbTypeName(DB_SQLITE), this);
    actionSqlite.setIcon(QIcon(DbViewerCommon::getConnectionIcon(DB_SQLITE, false)));
    connect(&actionSqlite, &QAction::triggered, this, &MainWindow::onAddNewConnectionSqlite);
    menu.addAction(&actionSqlite);

    menu.exec(QCursor::pos());
}

void MainWindow::onTreeItemDoubleClicked(QTreeWidgetItem *item, int column)
{
    TreeNodeType type = (TreeNodeType)item->data(column, Qt::UserRole + 1).toInt();
    if (type == NODE_CONNECTION)
    {
        if (!connectionDoubleClicked(item, column))// 打开连接失败时无法展开节点
        {
            qDebug() << "open connection failed";
            return;
        }
    }
    else if (type == NODE_DATABASE)
    {
        if (!databaseDoubleClicked(item, column))
        {
            qDebug() << "open database failed";
            return;
        }
    }
    else if (type == NODE_TABLE)
    {
        tableDoubleClicked(item, column);
    }
    ui->treeWidget->itemExpanded(item);
}

void MainWindow::onCustomContextMenuRequested(const QPoint &pos)
{
    QTreeWidgetItem *item = ui->treeWidget->itemAt(pos);
    if (item)
    {
        TreeNodeType nodeType = (TreeNodeType)item->data(0, Qt::UserRole + 1).toInt();
        if (nodeType == NODE_CONNECTION)// 数据库节点自定义菜单
        {
            QMenu menu(ui->treeWidget);
            QAction actionOpen("打开连接", ui->treeWidget);
            connect(&actionOpen, &QAction::triggered, this, &MainWindow::onOpenConnectionTriggered);
            menu.addAction(&actionOpen);
            QAction actionModify("编辑连接", ui->treeWidget);
            connect(&actionModify, &QAction::triggered, this, &MainWindow::onModifyConnectionTriggered);
            menu.addAction(&actionModify);
            menu.exec(QCursor::pos());
        }
        else if (nodeType == NODE_DATABASE)// 数据库节点自定义菜单
        {
            QMenu menu(ui->treeWidget);
            QAction actionOpen("打开数据库", ui->treeWidget);
            connect(&actionOpen, &QAction::triggered, this, &MainWindow::onOpenConnectionTriggered);
            menu.addAction(&actionOpen);
            menu.exec(QCursor::pos());
        }
        else if (nodeType == NODE_TABLE_PARENT)// 表根节点自定义菜单
        {
            QMenu menu(ui->treeWidget);
            QAction actionNewTable("新建表", ui->treeWidget);
            connect(&actionNewTable, &QAction::triggered, this, &MainWindow::onOpenConnectionTriggered);
            menu.addAction(&actionNewTable);
            menu.exec(QCursor::pos());
        }
        else if (nodeType == NODE_TABLE)// 表节点自定义菜单
        {
            QMenu menu(ui->treeWidget);
            QAction actionOpen("打开表", ui->treeWidget);
            connect(&actionOpen, &QAction::triggered, this, &MainWindow::onOpenConnectionTriggered);
            menu.addAction(&actionOpen);
            menu.exec(QCursor::pos());
        }
    }
}

void MainWindow::onModifyConnectionTriggered()
{

}

void MainWindow::onOpenConnectionTriggered()
{

}

void MainWindow::initSignalSlots()
{
    connect(ui->tabWidget, &QTabWidget::tabCloseRequested, this, &MainWindow::onTabCloseRequested);
    connect(ui->toolButtonNewConnection, &QToolButton::clicked, this, &MainWindow::onAddNewConnection);
    connect(ui->actionAccessMdb, &QAction::triggered, this, &MainWindow::onAddNewConnectionAccess);
    connect(ui->actionMySql, &QAction::triggered, this, &MainWindow::onAddNewConnectionMysql);
    connect(ui->actionSqlite, &QAction::triggered, this, &MainWindow::onAddNewConnectionSqlite);
    connect(ui->actionSQLServer, &QAction::triggered, this, &MainWindow::onAddNewConnectionSqlServer);

    connect(ui->treeWidget, &QTreeWidget::itemDoubleClicked, this, &MainWindow::onTreeItemDoubleClicked);
    connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this, &MainWindow::onCustomContextMenuRequested);
}

void MainWindow::initTreeWidget()
{
    loadConnections(DB_MYSQL);
    loadConnections(DB_SQLSERVER);
    loadConnections(DB_ACCESS);
    loadConnections(DB_SQLITE);
}

void MainWindow::initStatusBar()
{
    m_labelSql = new QLabel("sql", this);
    m_labelExeTime = new QLabel("0.00s", this);
    m_labelDataNum = new QLabel("no record", this);
    m_toolButtonShowList = new QToolButton(this);
    m_toolButtonShowDetail = new QToolButton(this);
    m_toolButtonShowEr = new QToolButton(this);
    m_toolButtonShowLeft = new QToolButton(this);
    m_toolButtonShowRight = new QToolButton(this);
    m_toolButtonShowList->setIcon(QIcon(":/res/list-detail.png"));
    m_toolButtonShowDetail->setIcon(QIcon(":/res/list-view.png"));
    m_toolButtonShowEr->setIcon(QIcon(":/res/table-er.png"));
    m_toolButtonShowLeft->setIcon(QIcon(":/res/menu-left.png"));
    m_toolButtonShowRight->setIcon(QIcon(":/res/menu-right.png"));
    ui->statusbar->addWidget(m_labelSql, 1);
    ui->statusbar->addWidget(m_labelExeTime);
    ui->statusbar->addWidget(m_labelDataNum);
    ui->statusbar->addWidget(m_toolButtonShowList);
    ui->statusbar->addWidget(m_toolButtonShowDetail);
    ui->statusbar->addWidget(m_toolButtonShowEr);
    ui->statusbar->addWidget(m_toolButtonShowLeft);
    ui->statusbar->addWidget(m_toolButtonShowRight);
}

void MainWindow::loadConnections(DataBaseType type)
{
    QString typeName = DbViewerCommon::getDbTypeName(type);
    int size = m_settings->beginReadArray(typeName);
    for (int i = 0; i < size; i++)
    {
        m_settings->setArrayIndex(i);
        QString connectionName = m_settings->value("connection").toString();
        addConnectionNode(type, connectionName);
    }
    m_settings->endArray();
}

void MainWindow::newConnection(DataBaseType type)
{
    NewConnectionDialog dialog(type, this);
    QString typeName = DbViewerCommon::getDbTypeName(type);
    dialog.setWindowTitle(QString("%1 - 新建连接").arg(typeName));
    if (dialog.exec() == QDialog::Accepted)
    {
        MSS info = dialog.getConnectionInfo();
        saveConnectionInfo(info, type);
        addConnectionNode(type, info["connection"]);
    }
}

bool MainWindow::saveConnectionInfo(const MSS &info, DataBaseType type, bool isNew)
{
    QString typeName = DbViewerCommon::getDbTypeName(type);
    QString connectionName = info["connection"];
    if (isNew && isRepeatConnection(connectionName))
    {
        QMessageBox::warning(this, "tip", "connection is existed");
        return false;
    }
    MSS data;
    int index = isNew ? getExistDbNum(type) : getConnectionInfo(type, connectionName, data);
    m_settings->beginWriteArray(typeName);
    m_settings->setArrayIndex(index);
    for (MSS::const_iterator iter = info.begin(); iter != info.end(); iter++)
    {
        qDebug() << QString("write to%1/%2 = %3").arg(typeName).arg(iter.key()).arg(iter.value());
        m_settings->setValue(iter.key(), iter.value());
    }
    m_settings->endArray();
    return true;
}

void MainWindow::addConnectionNode(DataBaseType type, const QString &connection)
{
    QTreeWidgetItem * item = new QTreeWidgetItem(QStringList() << connection);
    item->setData(0, Qt::UserRole + 2, type);
    ui->treeWidget->addTopLevelItem(item);
    QIcon icon;
    icon.addPixmap(QPixmap(DbViewerCommon::getConnectionIcon(type, false)));
    item->setIcon(0, icon);
}

bool MainWindow::isRepeatConnection(const QString &connectionName)
{
    for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
    {
        QTreeWidgetItem *item = ui->treeWidget->topLevelItem(i);
        QString connection = item->text(0);
        if (connection.compare(connectionName, Qt::CaseInsensitive) == 0)
        {
            qDebug() << "find repeat node:" << connection;;
            return true;
        }
    }
    return false;
}

QTreeWidgetItem *MainWindow::getTreeNode(QTreeWidgetItem *item, const QString &text)
{
    for (int i = 0; i < item->childCount(); i++)
    {
        QTreeWidgetItem *child = item->child(i);
        if (child->text(0).compare(text, Qt::CaseInsensitive) == 0)
        {
            return child;
        }
    }

    return NULL;
}

int MainWindow::getConnectionInfo(DataBaseType type, const QString &connection, MSS &info)
{
    QString typeName = DbViewerCommon::getDbTypeName(type);
    int existSize = m_settings->beginReadArray(typeName);
    int writeIndex = -1;
    for (int i = 0; i < existSize; i++)
    {
        m_settings->setArrayIndex(i);
        if (m_settings->value("connection").toString() == connection)
        {
            writeIndex = i;
            QStringList keys = m_settings->allKeys();
            qDebug() << "keys:" << keys.join(",");
            for (int t = 0; t < keys.size(); t++)
            {
                info[keys.at(t)] = m_settings->value(keys.at(t)).toString();
            }
            break;
        }
    }
    m_settings->endArray();

    return writeIndex;
}

int MainWindow::getExistDbNum(DataBaseType type)
{
    QString typeName = DbViewerCommon::getDbTypeName(type);
    int existSize = m_settings->beginReadArray(typeName);
    m_settings->endArray();
    return existSize;
}

bool MainWindow::connectionDoubleClicked(QTreeWidgetItem *item, int column)
{
    QString connection = item->text(column);
    if (m_openConnections[connection].size() > 0)
    {
        qDebug() << "connection is opened!!";
        return true;
    }
    DataBaseType type = (DataBaseType)item->data(column, Qt::UserRole + 2).toInt();
    qDebug() << "type:" << type;
    Database *db = new Database(this);
    QString dbName = "";
    VMSV datas;
    if (!openConnection(db, connection, type, dbName))
    {
        delete db;
        db = NULL;
        return false;
    }
    QString iconPath = ":/res/database-normal.png";
    if (type == DB_MYSQL)
    {
        QString sql = "show databases;";
        db->readRecords(sql, datas);
        for (int i = 0; i < datas.size(); i++)
        {
            addTreeNode(item, iconPath, datas[i]["Database"].toString(), NODE_DATABASE);
        }
        item->setIcon(0, QIcon(":/res/mysql-active.png"));
    }
    else if (type == DB_SQLSERVER)
    {
        QString sql = "Select Name FROM Master..SysDatabases orDER BY Name";
        db->readRecords(sql, datas);
        for (int i = 0; i < datas.size(); i++)
        {
            addTreeNode(item, iconPath, datas[i]["Name"].toString(), NODE_DATABASE);
        }
        item->setIcon(0, QIcon(":/res/mysql-active.png"));
    }
    else
    {
        addTreeNode(item, iconPath, "main", NODE_DATABASE);
        QString icon = DbViewerCommon::getConnectionIcon(type, true);
        item->setIcon(0, QIcon(icon));
    }
    QTreeWidgetItem * dbItem = getTreeNode(item, dbName);// 修改已打开的数据图标
    if (dbItem && !dbName.isEmpty())
        dbItem->setIcon(0, QIcon(":/res/database-active.png"));
    return true;
}

bool MainWindow::databaseDoubleClicked(QTreeWidgetItem *item, int column)
{
    QTreeWidgetItem *parent = item->parent();
    QString connection = parent->text(0);
    QString dbName = item->text(column);
    if (m_openConnections[connection][dbName])
    {
        qDebug() << "Batabase is opened!!";
        return true;
    }
    DataBaseType type = (DataBaseType)parent->data(column, Qt::UserRole + 2).toInt();
    MSS info;
    if (getConnectionInfo(type, connection, info) == -1)// 获取配置文件中的连接信息
    {
        qDebug() << QString("type:%1,connection:%2 not found").arg(DbViewerCommon::getDbTypeName(type)).arg(connection);
        return false;
    }
    info["dbName"] = dbName;
    Database *db = new Database(this);
    if (!db->connectToDb(type, info))
    {
        QMessageBox::information(this, "提示", "连接失败");
        return false;
    }
    m_openConnections[connection][dbName] = db;
    item->setIcon(0, QIcon(":/res/database-active.png"));
    addTreeNode(item, ":/res/table-active.png", "表", NODE_TABLE_PARENT);
    //addTreeNode(item, ":/res/view-active.png", "视图", NODE_VIEW_PARENT);
    addTableNodes(item, db, type);
    return true;
}

bool MainWindow::tableDoubleClicked(QTreeWidgetItem *item, int column)
{
    QTreeWidgetItem *dbNode = item->parent()->parent();
    QString database = dbNode->text(0);
    QString connection = dbNode->parent()->text(0);
    QString tableName = item->text(0);
    qDebug() << "connection:" << connection << ",database:" << database;
    Database *db = m_openConnections[connection][database];
    if (!db)
    {
        QMessageBox::information(this, "提示", "错误，数据库未连接");
        return false;
    }
    VMSV records;
    QString sql = QString("sqlect * from %1").arg(tableName);
    db->readRecords(tableName, "*", MSV(), records);
    if (records.size() > 0)
    {
        MSV record = records.at(0);
        QStringList fields = record.keys();
        TableWidget *widget = new TableWidget(sql, records, fields, this);
        int index = ui->tabWidget->addTab(widget, tableName);
        ui->tabWidget->setCurrentIndex(index);
        return true;
    }
    return false;
}

bool MainWindow::openConnection(Database *db, const QString &connection, DataBaseType type, QString &dbName)
{
    MSS info;
    if (getConnectionInfo(type, connection, info) == -1)// 获取配置文件中的连接信息
    {
        qDebug() << QString("type:%1,connection:%2 not found").arg(DbViewerCommon::getDbTypeName(type)).arg(connection);
        return false;
    }
    if (!db->connectToDb(type, info)) // 尝试连接数据库
    {
        QMessageBox::information(this, "提示", "连接失败");
        return false;
    }
    dbName = info["dbName"];
    if (!dbName.isEmpty())
    {
        m_openConnections[connection][dbName] = db;
    }

    return true;
}

bool MainWindow::addTreeNode(QTreeWidgetItem *parent, const QString &iconPath, const QString &text, TreeNodeType type)
{
    if (!parent || text.isEmpty() || type < 0 || getTreeNode(parent, text) != NULL)
    {
        return false;
    }
    QTreeWidgetItem * item = new QTreeWidgetItem(QStringList() << text);
    QIcon icon;
    icon.addPixmap(QPixmap(iconPath));
    item->setIcon(0, icon);
    item->setData(0, Qt::UserRole + 1, type);
    parent->addChild(item);
    return true;
}

void MainWindow::addTableNodes(QTreeWidgetItem *parent, Database *db, DataBaseType type)
{
    QTreeWidgetItem *tablesItem = getTreeNode(parent, "表");
    QString table = parent->text(0);
    if (tablesItem == NULL)
        return;
    VMSV records;
    if (type == DB_MYSQL)
    {
        db->readRecords("show tables", records);
        QString field = QString("Tables_in_%1").arg(table);
        for (int i = 0; i < records.size(); i++)
        {
            addTreeNode(tablesItem, ":/res/table-active.png", records[i][field].toString(), NODE_TABLE);
        }
    }
}

